home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2001 / MacHack 2001.toast / pc / The Hacks / pseudoDoc / headerDoc2HTML.pl < prev    next >
Encoding:
Perl Script  |  2001-06-23  |  29.3 KB  |  742 lines

  1. #!/usr/bin/perl
  2. #
  3. # Script name: headerDoc2HTML
  4. # Synopsis: Scans a file for headerDoc comments and generates an HTML
  5. #           file from the comments it finds.
  6. #
  7. # Author: Matt Morse (matt@apple.com)
  8. # Last Updated: $Date: 2001/06/06 18:02:45 $
  9. #
  10. # Copyright (c) 1999 Apple Computer, Inc.  All Rights Reserved.
  11. # The contents of this file constitute Original Code as defined in and are
  12. # subject to the Apple Public Source License Version 1.1 (the "License").
  13. # You may not use this file except in compliance with the License.  Please
  14. # obtain a copy of the License at http://www.apple.com/publicsource and
  15. # read it before using this file.
  16. #
  17. # This Original Code and all software distributed under the License are
  18. # distributed on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  19. # EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  20. # INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
  21. # FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the License for
  22. # the specific language governing rights and limitations under the
  23. # License.
  24. #
  25. # $Revision: 1.21 $
  26. #####################################################################
  27.  
  28. my $VERSION = 2.1;
  29. ################ General Constants ###################################
  30. my $isMacOS;
  31. my $pathSeparator;
  32. my $specifiedOutputDir;
  33. my $export;
  34. my $debugging;
  35. my $testingExport = 0;
  36. my $printVersion;
  37. #################### Locations #####################################
  38. # Look-up tables are used when exporting API and doc to tab-delimited
  39. # data files, which can be used for import to a database.  
  40. # The look-up tables supply uniqueID-to-APIName mappings.
  41.  
  42. my $scriptDir;
  43. my $lookupTableDirName;
  44. my $lookupTableDir;
  45. my $dbLookupTables;
  46. my $functionFilename;
  47. my $typesFilename;
  48. my $enumsFilename;
  49. my $masterTOCName;
  50. my @inputFiles;
  51.  
  52. # Check options in BEGIN block to avoid overhead of loading supporting 
  53. # modules in error cases.
  54. BEGIN {
  55.     use Cwd;
  56.     use Getopt::Std;
  57.     use File::Find;
  58.     
  59.     my %options = ();
  60.     $lookupTableDirName = "LookupTables";
  61.     $functionFilename = "functions.tab";;
  62.     $typesFilename = "types.tab";
  63.     $enumsFilename = "enumConstants.tab";
  64.  
  65.     $scriptDir = cwd();
  66.  
  67.     if ($^O =~ /MacOS/i) {
  68.             $pathSeparator = ":";
  69.             $isMacOS = 1;
  70.     } else {
  71.             $pathSeparator = "/";
  72.             $isMacOS = 0;
  73.     }
  74.     foreach (qw(Mac::Files)) {
  75.         $MOD_AVAIL{$_} = eval "use $_; 1";
  76.     }
  77.  
  78.     &getopts("dvxo:", \%options);
  79.     if ($options{v}) {
  80.         print "Getting version information for all modules.  Please wait...\n";
  81.         $printVersion = 1;
  82.         return;
  83.     }
  84.  
  85.     if ($options{d}) {
  86.             print "\tDebugging on...\n\n";
  87.             $debugging = 1;
  88.     }
  89.  
  90.     if ($options{o}) {
  91.         $specifiedOutputDir = $options{o};
  92.         if (! -e $specifiedOutputDir)  {
  93.             unless (mkdir ("$specifiedOutputDir", 0777)) {
  94.                 die "Error: $specifiedOutputDir does not exist. Exiting. \n$!\n";
  95.             }
  96.         } elsif (! -d $specifiedOutputDir) {
  97.             die "Error: $specifiedOutputDir is not a directory. Exiting.\n$!\n";
  98.         } elsif (! -w $specifiedOutputDir) {
  99.             die "Error: Output directory $specifiedOutputDir is not writable. Exiting.\n$!\n";
  100.         }
  101.         print "\nDocumentation will be written to $specifiedOutputDir\n";
  102.     }
  103.     $lookupTableDir = "$scriptDir$pathSeparator$lookupTableDirName";
  104.     if (($options{x}) || ($testingExport)) {
  105.         if ((-e "$lookupTableDir$pathSeparator$functionFilename") && (-e "$lookupTableDir$pathSeparator$typesFilename")) {
  106.                 print "\nWill write database files to an Export directory within each top-level HTML directory.\n\n";
  107.                 $export = 1;
  108.         } else {
  109.                 print "\nLookup table files not available. Cannot export data.\n";
  110.             $export = 0;
  111.                 $testingExport = 0;
  112.         }
  113.     }
  114.     
  115.     if (($#ARGV == 0) && (-d $ARGV[0])) {
  116.         my $inputDir = $ARGV[0];
  117.         $inputDir =~ s|(.*)/$|$1|; # get rid of trailing slash, if any
  118.         &find({wanted => \&getHeaders, follow => 1}, $inputDir);
  119.     } else {
  120.         print "Will process one or more individual files.\n" if ($debugging);
  121.         foreach my $singleFile (@ARGV) {
  122.             if (-f $singleFile) {
  123.                     push(@inputFiles, $singleFile);
  124.                 }
  125.         }
  126.     }
  127.     unless (@inputFiles) {
  128.         print "No valid input files specified. \n\n";
  129.         if ($isMacOS) {
  130.             die "\tTo use HeaderDoc, drop a header file or folder of header files on this application.\n\n";
  131.             } else {
  132.                     die "\tUsage: headerDoc2HTML [-d] [-o <output directory>] <input file(s) or directory>.\n\n";
  133.             }
  134.     }
  135.     
  136.     sub getHeaders {
  137.         my $filePath = $File::Find::name;
  138.         my $fileName = $_;
  139.         
  140.         if ($fileName =~ /\.h$/) {
  141.             push(@inputFiles, $filePath);
  142.         }
  143.     }
  144. }
  145.  
  146.  
  147. use strict;
  148. use File::Copy;
  149. use FindBin qw ($Bin);
  150. use lib "$Bin". "$pathSeparator"."Modules";
  151.  
  152. # Classes and other modules specific to HeaderDoc
  153. use HeaderDoc::DBLookup;
  154. use HeaderDoc::Utilities qw(findRelativePath safeName getAPINameAndDisc printArray printHash updateHashFromConfigFiles getHashFromConfigFile);
  155. use HeaderDoc::Header;
  156. use HeaderDoc::CPPClass;
  157. use HeaderDoc::ObjCClass;
  158. use HeaderDoc::Function;
  159. use HeaderDoc::Typedef;
  160. use HeaderDoc::Struct;
  161. use HeaderDoc::Constant;
  162. use HeaderDoc::Var;
  163. use HeaderDoc::PDefine;
  164. use HeaderDoc::Enum;
  165. use HeaderDoc::MinorAPIElement;
  166.  
  167. ################ Setup from Configuration File #######################
  168. my $localConfigFileName = "headerDoc2HTML.config";
  169. my $preferencesConfigFileName = "com.apple.headerDoc2HTML.config";
  170. my $homeDir = (getpwuid($<))[7];
  171. my $usersPreferencesPath = $homeDir.$pathSeparator."Library".$pathSeparator."Preferences";
  172.  
  173. # The order of files in this array determines the order that the config files will be read
  174. # If there are multiple config files that declare a value for the same key, the last one read wins
  175. my @configFiles = ($usersPreferencesPath.$pathSeparator.$preferencesConfigFileName, $Bin.$pathSeparator.$localConfigFileName);
  176.  
  177. # default configuration, which will be modified by assignments found in config files.
  178. my %config = (
  179.     copyrightOwner => "",
  180.     defaultFrameName => "index.html",
  181.     compositePageName => "CompositePage.html",
  182.     masterTOCName => "MasterTOC.html",
  183.     apiUIDPrefix => "apple_ref"
  184. );
  185.  
  186. %config = &updateHashFromConfigFiles(\%config,\@configFiles);
  187.  
  188. if (defined $config{"copyrightOwner"}) {
  189.     HeaderDoc::APIOwner->copyrightOwner($config{"copyrightOwner"});
  190. }
  191. if (defined $config{"defaultFrameName"}) {
  192.     HeaderDoc::APIOwner->defaultFrameName($config{"defaultFrameName"});
  193. }
  194. if (defined $config{"compositePageName"}) {
  195.     HeaderDoc::APIOwner->compositePageName($config{"compositePageName"});
  196. }
  197. if (defined $config{"apiUIDPrefix"}) {
  198.     HeaderDoc::APIOwner->apiUIDPrefix($config{"apiUIDPrefix"});
  199. }
  200.  
  201. ################ Version Info ##############################
  202. if ($printVersion) {
  203.     &printVersionInfo();
  204.     exit;
  205.  
  206. ################ Exporting ##############################
  207. if ($export || $testingExport) {
  208.     HeaderDoc::DBLookup->loadUsingFolderAndFiles($lookupTableDir, $functionFilename, $typesFilename, $enumsFilename);
  209. }
  210.  
  211. ################### States ###########################################
  212. my $inHeader = 0;
  213. my $inCPPHeader = 0;
  214. my $inClass = 0;
  215. my $inFunction = 0;
  216. my $inTypedef = 0;
  217. my $inStruct = 0;
  218. my $inConstant = 0;
  219. my $inVar = 0;
  220. my $inPDefine = 0;
  221. my $inEnum = 0;
  222.  
  223. ################ Processing starts here ##############################
  224. my $headerObject;  # this is the Header object that will own the HeaderElement objects for this file.
  225.  
  226. foreach my $inputFile (@inputFiles) {
  227.     my $constantObj;
  228.     my $enumObj;
  229.     my $funcObj;
  230.     my $pDefineObj;
  231.     my $structObj;
  232.     my $typedefObj;
  233.     my $varObj;
  234.     my $cppAccessControlState = "protected:"; # the default in C++
  235.     
  236.     my @path = split (/$pathSeparator/, $inputFile);
  237.     my $filename = pop (@path);
  238.     print "\nProcessing $filename\n";
  239.     
  240.     my $headerDir = join("$pathSeparator", @path);
  241.     my $rootFileName;
  242.     ($rootFileName = $filename) =~ s/\.(h|i)$//;
  243.     my $rootOutputDir;
  244.     if (length ($specifiedOutputDir)) {
  245.         $rootOutputDir ="$specifiedOutputDir$pathSeparator$rootFileName";
  246.     } elsif (@path) {
  247.         $rootOutputDir ="$headerDir$pathSeparator$rootFileName";
  248.     } else {
  249.         $rootOutputDir = $rootFileName;
  250.     }
  251.         
  252.     open(INPUTFILE, "<$inputFile") || die "Can't open input file $inputFile.\n$!\n";
  253.     my @rawInputLines = <INPUTFILE>;
  254.     close INPUTFILE;
  255.     
  256.     # check for HeaderDoc comments -- if none, move to next file
  257.     my @headerDocCommentLines = grep(/^\s*\/\*\!/, @rawInputLines);
  258.     if (!@headerDocCommentLines) {
  259.         print "    Skipping. No HeaderDoc comments found.\n";
  260.         next;
  261.     }
  262.     
  263.     $headerObject = HeaderDoc::Header->new();
  264.     $headerObject->outputDir($rootOutputDir);
  265.     $headerObject->name($filename);
  266.     
  267.     # scan input lines for class declarations
  268.     # return an array of array refs, the first array being the header-wide lines
  269.     # the others (if any) being the class-specific lines
  270.     my @lineArrays = &getLineArrays(\@rawInputLines);
  271.     
  272.     foreach my $arrayRef (@lineArrays) {
  273.         my @inputLines = @$arrayRef; 
  274.         # look for /*! comments and collect all comment fields into a the appropriate objects
  275.         my $apiOwner = $headerObject;  # switches to a class object, when within a class declaration
  276.         my $inputCounter = 0;
  277.         while ($inputCounter <= $#inputLines) {
  278.             my $localDebug = 0;
  279.             my $line = "";           
  280.             
  281.             print "Input line number: $inputCounter\n" if ($localDebug);
  282.             if ($inputLines[$inputCounter] =~ /^\s*(public:|private:|protected:)/) {$cppAccessControlState = $&;}
  283.             if ($inputLines[$inputCounter] =~ /^\s*\/\*\!/) {  # entering headerDoc comment
  284.                 # slurp up comment as line
  285.                 if ($inputLines[$inputCounter] =~ /\s*\*\//) { # closing comment marker on same line
  286.                     $line .= $inputLines[$inputCounter++];
  287.                     print "Input line number: $inputCounter\n" if ($localDebug);
  288.                 } else {                                       # multi-line comment
  289.                     do {
  290.                         $inputLines[$inputCounter] =~ s/^[\t ]*[*]?[\t ]+(.*)$/$1/; # remove leading whitespace, and any leading asterisks
  291.                         $line .= $inputLines[$inputCounter++];
  292.                         print "Input line number: $inputCounter\n" if ($localDebug);
  293.                     } while (($inputLines[$inputCounter] !~ /\*\//) && ($inputCounter <= $#inputLines));
  294.                     $line .= $inputLines[$inputCounter++];     # get the closing comment marker
  295.                     print "Input line number: $inputCounter\n" if ($localDebug);
  296.                 }
  297.                 $line =~ s/^\s+//;              # trim leading whitespace
  298.                 $line =~ s/^(.*)\*\/\s*$/$1/s;  # remove closing comment marker
  299.                
  300.                SWITCH: { # determine which type of comment we're in
  301.                     ($line =~ /^\/\*!\s+\@header\s*/i) && do {$inHeader = 1; last SWITCH;};
  302.                     ($line =~ /^\/\*!\s+\@class\s*/i) && do {$inClass = 1;last SWITCH;};
  303.                      ($line =~ /^\/\*!\s+\@language\s+.*c\+\+\s*/i) && do {$inCPPHeader = 1; last SWITCH;};
  304.                     ($line =~ /^\/\*!\s+\@function\s*/i) && do {$inFunction = 1;last SWITCH;};
  305.                     ($line =~ /^\/\*!\s+\@typedef\s*/i) && do {$inTypedef = 1;last SWITCH;};
  306.                     ($line =~ /^\/\*!\s+\@struct\s*/i) && do {$inStruct = 1;last SWITCH;};
  307.                     ($line =~ /^\/\*!\s+\@const(ant)?\s*/i) && do {$inConstant = 1;last SWITCH;};
  308.                     ($line =~ /^\/\*!\s+\@var\s*/i) && do {$inVar = 1;last SWITCH;};
  309.                     ($line =~ /^\/\*!\s+\@define(d)?\s*/i) && do {$inPDefine = 1;last SWITCH;};
  310.                     ($line =~ /^\/\*!\s+\@enum\s*/i) && do {$inEnum = 1;last SWITCH;};
  311.                     print "HeaderDoc comment is not of known type. Comment text is:\n";
  312.                     print "$line\n";
  313.                }
  314.                 $line =~ s/\n\n/\n<br><br>\n/g; # change newline pairs into HTML breaks, for para formatting
  315.                 my @fields = split(/\@/, $line);
  316.                 if ($inCPPHeader) {print "inCPPHeader\n" if ($debugging); &processCPPHeaderComment();};
  317.                 if ($inClass) {print "inClass\n" if ($debugging); $apiOwner = &processClassComment($apiOwner, $rootOutputDir, \@fields);};
  318.                 if ($inHeader) {print "inHeader\n" if ($debugging); $apiOwner = &processHeaderComment($apiOwner, $rootOutputDir, \@fields);};
  319.                 if ($inFunction) {
  320.                     print "inFunction\n" if ($localDebug);
  321.                     $funcObj = HeaderDoc::Function->new;
  322.                     $funcObj->processFunctionComment(\@fields);
  323.                      while (($inputLines[$inputCounter] !~ /\w/)  && ($inputCounter <= $#inputLines)){ 
  324.                          $inputCounter++;
  325.                          print "Input line number: $inputCounter\n" if ($localDebug);
  326.                     }; # move beyond blank lines
  327.                     
  328.                     if ($inputLines[$inputCounter] =~ /^#define/) { # if function-like macro
  329.                         my $declaration = $inputLines[$inputCounter];
  330.                         while (($declaration =~ /\\\n$/)  && ($inputCounter <= $#inputLines)){ 
  331.                             $inputCounter++;
  332.                             $declaration .= $inputLines[$inputCounter];
  333.                             print "Input line number: $inputCounter\n" if ($localDebug);
  334.                         }; # get escaped-newline lines
  335.                         $funcObj->setFunctionDeclaration($declaration);
  336.                     } else { # if regular function
  337.                         my $declaration = $inputLines[$inputCounter];
  338.                         if ($declaration !~ /;[^;]*$/) { # search for semicolon end, even with trailing comment
  339.                             do { 
  340.                                 $inputCounter++;
  341.                                 print "Input line number: $inputCounter\n" if ($localDebug);
  342.                                 $declaration .= $inputLines[$inputCounter];
  343.                             } while (($declaration !~ /;[^;]*$/)  && ($inputCounter <= $#inputLines))
  344.                         }
  345.                         $declaration =~ s/^\s+//g;                # trim leading spaces.
  346.                         $declaration =~ s/([^;]*;).*$/$1/s;        # trim anything following the final semicolon, 
  347.                                                                 # including comments.
  348.                         $declaration =~ s/([^{]+){.*;$/$1;/s;   # handle inline functions [#2423551]
  349.                                                                 # by removing opening brace up to semicolon
  350.                         $declaration =~ s/\s+;/;/;                # trim spaces before semicolon.
  351.                         if ($declaration =~ /^virtual.*/) {
  352.                             $funcObj->linkageState("virtual");
  353.                         } elsif ($declaration =~ /^static.*/) {
  354.                             $funcObj->linkageState("static");
  355.                         } else {
  356.                             $funcObj->linkageState("other");
  357.                         }
  358.                         $funcObj->setFunctionDeclaration($declaration);
  359.                     }
  360.                     if (length($funcObj->name())) {
  361.                         if (ref($apiOwner) ne "HeaderDoc::Header") {
  362.                             $funcObj->accessControl($cppAccessControlState);
  363.                         }
  364.                         $apiOwner->addToFunctions($funcObj);
  365.                     }
  366.                 }
  367.                 if ($inTypedef) {
  368.                     $typedefObj = HeaderDoc::Typedef->new;
  369.                     $typedefObj->processTypedefComment(\@fields);
  370.                     my $typedefName = $typedefObj->name();
  371.                     
  372.                      while (($inputLines[$inputCounter] !~ /\w/)  && ($inputCounter <= $#inputLines)){
  373.                          $inputCounter++;
  374.                         print "Input line number: $inputCounter\n" if ($localDebug);
  375.                      };
  376.                     my  $declaration = $inputLines[$inputCounter];
  377.                     # if a struct declaration precedes the typedef, suck it up
  378.                     if ($declaration !~ /typedef/) {
  379.                         while (($inputLines[$inputCounter] !~ /typedef/) && ($inputCounter <= $#inputLines)){
  380.                             $declaration .= $inputLines[++$inputCounter];
  381.                             print "Input line number: $inputCounter\n" if ($localDebug);
  382.                         };
  383.                     } else {
  384.                         if ($declaration =~ /{/) { # taking care of a typedef'd block
  385.                             while (($inputLines[$inputCounter] !~ /}/) && ($inputCounter <= $#inputLines)) {$declaration .= $inputLines[++$inputCounter]; print "Input line number: $inputCounter\n" if ($localDebug);};
  386.                         } else {
  387.                             if ($declaration !~ /$typedefName/) { # find type name at end of declaration
  388.                                 while (($inputLines[$inputCounter] !~ /$typedefName/) && ($inputCounter <= $#inputLines)){
  389.                                     $declaration .= $inputLines[++$inputCounter]; 
  390.                                     print "Input line number: $inputCounter\n" if ($localDebug);
  391.                                 }
  392.                             } else { # find final semicolon
  393.                                 while (($inputLines[$inputCounter] !~ /;/) && ($inputCounter <= $#inputLines)){
  394.                                     $declaration .= $inputLines[++$inputCounter]; 
  395.                                     print "Input line number: $inputCounter\n" if ($localDebug);
  396.                                 }
  397.                             }
  398.                         }
  399.                     }
  400.                     if (length($declaration)) {
  401.                         $typedefObj->setTypedefDeclaration($declaration);
  402.                     } else {
  403.                         warn "Couldn't find a declaration for typedef near line: $inputCounter\n";
  404.                     }
  405.                     if (length($typedefObj->name())) {
  406.                         if (ref($apiOwner) ne "HeaderDoc::Header") {
  407.                             $typedefObj->accessControl($cppAccessControlState);
  408.                             $apiOwner->addToVars($typedefObj);
  409.                         } else { # headers group by type
  410.                             $apiOwner->addToTypedefs($typedefObj);
  411.                         }
  412.                     }
  413.                 }  ## end inTypedef
  414.                 
  415.                 if ($inStruct) { 
  416.                     $structObj = HeaderDoc::Struct->new;
  417.                     $structObj->processStructComment(\@fields);
  418.                      while (($inputLines[$inputCounter] !~ /\w/)  && ($inputCounter <= $#inputLines)){ $inputCounter++; print "Input line number: $inputCounter\n" if ($localDebug);}; # move beyond blank lines
  419.                     my  $declaration = $inputLines[$inputCounter];
  420.                     while ($inputLines[$inputCounter] !~ /}/) {$declaration .= $inputLines[++$inputCounter]; print "Input line number: $inputCounter\n" if ($localDebug);}; # simplistic
  421.                     if (length($declaration)) {
  422.                         $structObj->setStructDeclaration($declaration);
  423.                     } else {
  424.                         warn "Couldn't find a declaration for struct near line: $inputCounter\n";
  425.                     }
  426.                     if (length($structObj->name())) {
  427.                         if (ref($apiOwner) ne "HeaderDoc::Header") {
  428.                             $structObj->accessControl($cppAccessControlState);
  429.                             $apiOwner->addToVars($structObj);
  430.                         } else { # headers group by type
  431.                             $apiOwner->addToStructs($structObj);
  432.                         }
  433.                     }
  434.                 }  ## end inStruct
  435.                           
  436.                 if ($inConstant) {
  437.                     $constantObj = HeaderDoc::Constant->new;
  438.                     $constantObj->processConstantComment(\@fields);
  439.                     while (($inputLines[$inputCounter] !~ /\w/)  && ($inputCounter <= $#inputLines)){$inputCounter++; print "Input line number: $inputCounter\n" if ($localDebug);};
  440.                     if ($inputLines[$inputCounter] =~ /^\s*(const|extern|CF_EXPORT)/) {
  441.                        my $declaration = $inputLines[$inputCounter];
  442.                        if ($declaration !~ /;\s*$/) {
  443.                            $inputCounter++;
  444.                            print "Input line number: $inputCounter\n" if ($localDebug);
  445.                            while (($declaration !~ /\s*;\s*$/) && ($inputCounter <= $#inputLines)){$declaration .= $inputLines[$inputCounter++];print "Input line number: $inputCounter\n" if ($localDebug);};
  446.                        }
  447.                        $declaration =~ s/CF_EXPORT\s+//;
  448.                        $constantObj->setConstantDeclaration($declaration);
  449.                     }
  450.                     if (length($constantObj->name())) {
  451.                         if (ref($apiOwner) ne "HeaderDoc::Header") {
  452.                             $constantObj->accessControl($cppAccessControlState);
  453.                             $apiOwner->addToVars($constantObj);
  454.                         } else { # headers group by type
  455.                             $apiOwner->addToConstants($constantObj);
  456.                         }
  457.                     }
  458.                 } ## end inConstant
  459.                 
  460.                 if ($inVar) {
  461.                     $varObj = HeaderDoc::Var->new;
  462.                     $varObj->processVarComment(\@fields);
  463.                     while (($inputLines[$inputCounter] !~ /\w/)  && ($inputCounter <= $#inputLines)){  $inputCounter++; print "Input line number: $inputCounter\n" if ($localDebug);};
  464.                     my $declaration = &removeSlashSlashComment($inputLines[$inputCounter]);
  465.                     if ($declaration !~ /;\s*$/) {
  466.                         $inputCounter++;
  467.                         print "Input line number: $inputCounter\n" if ($localDebug);
  468.                         while (($declaration !~ /}\s*;\s*$/)  && ($inputCounter <= $#inputLines)){$declaration .= &removeSlashSlashComment($inputLines[$inputCounter++]); print "Input line number: $inputCounter\n" if ($localDebug);};
  469.                     }
  470.                     $varObj->setVarDeclaration($declaration);
  471.                     if (length($varObj->name())) {
  472.                         if (ref($apiOwner) ne "HeaderDoc::Header") {
  473.                             $varObj->accessControl($cppAccessControlState);
  474.                             $apiOwner->addToVars($varObj);
  475.                         } else { # headers group by type
  476.                             warn "### \@var tag found outside of a class declaration. \n";
  477.                             $varObj->printObject();
  478.                             $apiOwner->addToVars($varObj);  # we add it anyway
  479.                         }
  480.                     }
  481.                 } ## end inVar
  482.                 
  483.                 if ($inPDefine) {
  484.                     $pDefineObj = HeaderDoc::PDefine->new;
  485.                     $pDefineObj->processPDefineComment(\@fields);
  486.                     while (($inputLines[$inputCounter] !~ /\w/) && ($inputCounter <= $#inputLines)){  $inputCounter++;print "Input line number: $inputCounter\n" if ($localDebug);};
  487.                     my $declaration;
  488.                     if ($inputLines[$inputCounter] =~ /^\s*#define/) {
  489.                         while (($inputLines[$inputCounter] =~ /^\s*#define/) && ($inputCounter <= $#inputLines)){
  490.                             $declaration .= $inputLines[$inputCounter];
  491.                             if ($declaration =~ /\\\n$/) {  # escaped newlines
  492.                                 while (($declaration =~ /\\\n$/) && ($inputCounter <= $#inputLines)){$inputCounter++; $declaration .= $inputLines[$inputCounter];print "Input line number: $inputCounter\n" if ($localDebug);};
  493.                             }
  494.                             $inputCounter++;
  495.                             print "Input line number: $inputCounter\n" if ($localDebug);
  496.                         }
  497.                     } else { 
  498.                         warn "Can't find declaration for \@define comment with name:\n";
  499.                         my $name = $pDefineObj->name();
  500.                         print "$name\n\n";
  501.                     }
  502.                     $pDefineObj->setPDefineDeclaration($declaration);
  503.                     
  504.                     if (length($pDefineObj->name())) {
  505.                         if (ref($apiOwner) ne "HeaderDoc::Header") {
  506.                             $pDefineObj->accessControl($cppAccessControlState);
  507.                             $apiOwner->addToVars($pDefineObj);
  508.                         } else { # headers group by type
  509.                             $apiOwner->addToPDefines($pDefineObj);
  510.                         }
  511.                     }                    
  512.                 } ## end inPDefine
  513.                 
  514.                 if ($inEnum) {
  515.                     $enumObj = HeaderDoc::Enum->new;
  516.                     $enumObj->processEnumComment(\@fields);
  517.                      while (($inputLines[$inputCounter] !~ /\w/) && ($inputCounter <= $#inputLines)){ $inputCounter++;print "Input line number: $inputCounter\n" if ($localDebug);};  # move beyond blank lines
  518.                     my  $declaration = $inputLines[$inputCounter];
  519.                     while (($inputLines[$inputCounter] !~ /}/) && ($inputCounter <= $#inputLines)){$declaration .= $inputLines[++$inputCounter];print "Input line number: $inputCounter\n" if ($localDebug);}; # simplistic
  520.                     
  521.                     if (length($declaration)) {
  522.                         $enumObj->declarationInHTML($enumObj->getEnumDeclaration($declaration));
  523. #                     my $s = $enumObj->declaration;
  524. #                     print "Enum dec is:\n";
  525. #                     print "|$s|\n";
  526.                     } else {
  527.                         warn "Couldn't find a declaration for enum near line: $inputCounter\n";
  528.                     }
  529.                     if (length($enumObj->name())) {
  530.                         if (ref($apiOwner) ne "HeaderDoc::Header") {
  531.                             $enumObj->accessControl($cppAccessControlState);
  532.                             $apiOwner->addToVars($enumObj);
  533.                         } else { # headers group by type
  534.                             $apiOwner->addToEnums($enumObj);
  535.                         }
  536.                     }
  537.                 }  ## end inEnum
  538.             }
  539.             $inHeader = $inClass = $inFunction = $inTypedef = $inStruct = $inConstant = $inVar = $inPDefine = $inEnum = 0;
  540.             $inputCounter++;
  541.             print "Input line number: $inputCounter\n" if ($localDebug);
  542.         } # end processing individual line array
  543.         
  544.         if (ref($apiOwner) ne "HeaderDoc::Header") { # if we've been filling a class object, add it to the header
  545.             my $name = $apiOwner->name();
  546.             my $refName = ref($apiOwner);
  547.             $headerObject->addToClasses($apiOwner);
  548.         }
  549.     } # end processing array of line arrays
  550.     $headerObject->createFramesetFile();
  551.     $headerObject->createContentFile();
  552.     $headerObject->createTOCFile();
  553.     $headerObject->writeHeaderElements();
  554.     $headerObject->writeHeaderElementsToCompositePage();
  555.     $headerObject->writeExportsWithName($rootFileName) if (($export) || ($testingExport));
  556. }
  557. print "...done\n";
  558. exit 0;
  559.  
  560.  
  561. #############################  Subroutines ###################################
  562.  
  563. sub getLineArrays {
  564.     my $rawLineArrayRef = shift;
  565.     my @arrayOfLineArrays = ();
  566.     my @generalHeaderLines = ();
  567.     
  568.     my $inputCounter = 0;
  569.     my $lastArrayIndex = @{$rawLineArrayRef};
  570.     my $line = "";
  571.     my $className = "";
  572.     
  573.     while ($inputCounter <= $lastArrayIndex) {
  574.         $line = ${$rawLineArrayRef}[$inputCounter];
  575.         
  576.         # we're entering a headerdoc comment--look ahead for @class tag
  577.         if (($line =~ /^\/\*\!/)) {
  578.             my $headerDocComment = "";
  579.             {
  580.                 local $^W = 0;  # turn off warnings since -w is overly sensitive here
  581.                 while (($line !~ /\*\//) && ($inputCounter <= $lastArrayIndex)) {
  582.                     $line =~ s/^[ \t]*//; # remove leading whitespace
  583.                     $line =~ s/^[*]\s*$/\n/; # replace sole asterisk with paragraph divider
  584.                     $line =~ s/^[*]\s+(.*)/$1/; # remove asterisks that precede text
  585.                     $headerDocComment .= $line;
  586.                     $line = ${$rawLineArrayRef}[++$inputCounter];
  587.                 }
  588.             }
  589.             
  590.             # test for @class comment
  591.             # here is where we create an array of class-specific lines
  592.             # first, get the class name
  593.             if ($headerDocComment =~ /^\/\*!\s+\@class\s*/i) {
  594.                my @classLines;
  595.                
  596.                ($className = $headerDocComment) =~ s/.*\@class\s+(\w+)\s+.*/$1/s;
  597.                push (@classLines, $headerDocComment);
  598.                while (($line !~ /{/) && ($inputCounter <= $lastArrayIndex)) {
  599.                       $line = ${$rawLineArrayRef}[$inputCounter];
  600.                    push (@classLines, $line);
  601.                    $inputCounter++;
  602.                }
  603.                # now we're at the opening brace of the class declaration
  604.                # push it into the array
  605.                $line = ${$rawLineArrayRef}[$inputCounter];
  606.                push (@classLines, $line);
  607.                $inputCounter++;
  608.                
  609.                # now collect class lines until
  610.                my $inClassBraces = 1;
  611.                my $leftBraces;
  612.                my $rightBraces;
  613.                while ($inClassBraces) {
  614.                    $line = ${$rawLineArrayRef}[$inputCounter];
  615.                    push (@classLines, $line);
  616.                    $leftBraces = $line =~ tr/{//;
  617.                    $rightBraces = $line =~ tr/}//;
  618.                    $inClassBraces += $leftBraces;
  619.                    $inClassBraces -= $rightBraces;
  620.                    $inputCounter++;
  621.                }
  622.                 push (@arrayOfLineArrays, \@classLines);
  623.  
  624.             } else {
  625.                 push (@generalHeaderLines, $headerDocComment);
  626.             }
  627.         }
  628.         push (@generalHeaderLines, $line);
  629.         $inputCounter++;
  630.     }
  631.     push (@arrayOfLineArrays, \@generalHeaderLines);
  632.     return @arrayOfLineArrays;
  633. }
  634.  
  635. sub processCPPHeaderComment {
  636. #     for now, we do nothing with this comment
  637.     return;
  638. }
  639.  
  640. sub removeSlashSlashComment {
  641.     my $line = shift;
  642.     $line =~ s/\/\/.*$//;
  643.     return $line;
  644. }
  645.  
  646. sub processClassComment {
  647.     my $apiOwner = shift;
  648.     my $headerObj = $apiOwner;
  649.     my $rootOutputDir = shift;
  650.     my $fieldArrayRef = shift;
  651.     my @fields = @$fieldArrayRef;
  652.     
  653.     $apiOwner = HeaderDoc::CPPClass->new;
  654.     $apiOwner->headerObject($headerObj);
  655.     $apiOwner->outputDir($rootOutputDir);
  656.     foreach my $field (@fields) {
  657.         SWITCH: {
  658.             ($field =~ /^\/\*\!/)&& do {last SWITCH;}; # ignore opening /*!
  659.             ($field =~ s/^class\s+//) && 
  660.             do {
  661.                 my ($name, $disc);
  662.                 ($name, $disc) = &getAPINameAndDisc($field); 
  663.                 $apiOwner->name($name);
  664.                 if (length($disc)) {$apiOwner->discussion($disc);};
  665.                 last SWITCH;
  666.             };
  667.             ($field =~ s/^abstract\s+//) && do {$apiOwner->abstract($field); last SWITCH;};
  668.             ($field =~ s/^discussion\s+//) && do {$apiOwner->discussion($field); last SWITCH;};
  669.             print "Unknown field in class comment: $field\n";
  670.         }
  671.     }
  672.     return $apiOwner;
  673. }
  674.  
  675. sub processHeaderComment {
  676.     my $apiOwner = shift;
  677.     my $rootOutputDir = shift;
  678.     my $fieldArrayRef = shift;
  679.     my @fields = @$fieldArrayRef;
  680.  
  681.     foreach my $field (@fields) {
  682.         # print "header field: |$field|\n";
  683.         SWITCH: {
  684.             ($field =~ /^\/\*\!/)&& do {last SWITCH;}; # ignore opening /*!
  685.             ($field =~ s/^header\s+//) && 
  686.             do {
  687.                 my ($name, $disc);
  688.                 ($name, $disc) = &getAPINameAndDisc($field); 
  689.                 print "Setting header name to $name\n" if ($debugging);
  690.                 print "Discussion is:\n" if ($debugging);
  691.                 print "$disc\n" if ($debugging);
  692.                 if (length($disc)) {$apiOwner->discussion($disc);};
  693.                 last SWITCH;
  694.             };
  695.             ($field =~ s/^abstract\s+//) && do {$apiOwner->abstract($field); last SWITCH;};
  696.             ($field =~ s/^discussion\s+//) && do {$apiOwner->discussion($field); last SWITCH;};
  697.             print "Unknown field in header comment: $field\n";
  698.         }
  699.     }
  700.     return $apiOwner;
  701. }
  702.  
  703. sub printVersionInfo {
  704.     my $av = HeaderDoc::APIOwner->VERSION();
  705.     my $hev = HeaderDoc::HeaderElement->VERSION();
  706.     my $hv = HeaderDoc::Header->VERSION();
  707.     my $cppv = HeaderDoc::CPPClass->VERSION();
  708.     my $fv = HeaderDoc::Function->VERSION();
  709.     my $tv = HeaderDoc::Typedef->VERSION();
  710.     my $sv = HeaderDoc::Struct->VERSION();
  711.     my $cv = HeaderDoc::Constant->VERSION();
  712.     my $vv = HeaderDoc::Var->VERSION();
  713.     my $ev = HeaderDoc::Enum->VERSION();
  714.     my $uv = HeaderDoc::Utilities->VERSION();
  715.     my $me = HeaderDoc::MinorAPIElement->VERSION();
  716.     
  717.     print "----------------------------------------------------\n";
  718.     print "\tHeaderDoc version $VERSION.\n";
  719.     print "\tModules:\n";
  720.     print "\t\tAPIOwner - $av\n";
  721.     print "\t\tHeaderElement - $hev\n";
  722.     print "\t\tHeader - $hv\n";
  723.     print "\t\tCPPClass - $cppv\n";
  724.     print "\t\tFunction - $fv\n";
  725.     print "\t\tTypedef - $tv\n";
  726.     print "\t\tStruct - $sv\n";
  727.     print "\t\tConstant - $cv\n";
  728.     print "\t\tEnum - $ev\n";
  729.     print "\t\tVar - $vv\n";
  730.     print "\t\tMinorAPIElement - $me\n";
  731.     print "\t\tUtilities - $uv\n";
  732.     print "----------------------------------------------------\n";
  733. }
  734.  
  735. ################################################################################
  736. # Version Notes
  737. # 1.61 (02/24/2000) Fixed getLineArrays to respect paragraph breaks in comments that 
  738. #                   have an asterisk before each line.
  739. ################################################################################
  740.  
  741.